package com.arpa.wms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.jpush.api.report.UsersResult;
import com.arpa.core.web.Result;
import com.arpa.ntocc.common.common.cache.PartyCache;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.CommonUtil;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.wms.cache.GoodsCache;
import com.arpa.wms.cache.ImportTempCache;
import com.arpa.wms.domain.consts.NumConst;
import com.arpa.wms.domain.dto.ContainerDTO;
import com.arpa.wms.domain.dto.ContainerExcelDTO;
import com.arpa.wms.domain.entity.Container;
import com.arpa.wms.domain.entity.ReceiveRegister;
import com.arpa.wms.domain.enums.ContainerTypeEnum;
import com.arpa.wms.domain.enums.DeleteFlagEnum;
import com.arpa.wms.domain.enums.StatusEnum;
import com.arpa.wms.domain.vo.ContainerVO;
import com.arpa.wms.mapper.ContainerMapper;
import com.arpa.wms.service.IContainerService;
import com.arpa.wms.service.IReceiveRegisterService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static com.arpa.wms.domain.consts.NumConst.*;
import static com.arpa.wms.domain.consts.TipConst.*;

/**
 * <p>
 * 容器 服务实现类
 * </p>
 *
 * @author LEO
 * @since 2020-08-03
 */
@Service
public class ContainerServiceImpl extends ServiceImpl<ContainerMapper, Container> implements IContainerService {

    private final ImportTempCache importTempCache;
    private final IReceiveRegisterService receiveRegisterService;
    private final GoodsCache goodsCache;
    private final PartyCache partyCache;

    public ContainerServiceImpl(ImportTempCache importTempCache, @Lazy IReceiveRegisterService receiveRegisterService,
                                GoodsCache goodsCache, PartyCache partyCache) {
        this.importTempCache = importTempCache;
        this.receiveRegisterService = receiveRegisterService;
        this.goodsCache = goodsCache;
        this.partyCache = partyCache;
    }
    private static final String THE_CONTAINER_NUMBER_ALREADY_EXISTS = "容器类型已存在!";
    /**
     * 保存
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor={Exception.class})
    public boolean save(Container entity) {
        List<ContainerVO> containerVos = queryByBarCode(entity.getBarCode());
        if(null != containerVos && containerVos.size() > 0){
            throw new ServiceException("容器编号已存在！");
        }
        checkCustomerCodeAndCustomerName(entity);
        entity.setCode(IdUtil.simpleUUID());
        entity.setGroupCode(UserUtil.getBranchCode());
        entity.setStatus(StatusEnum.UNACTIVE.getValue());
        entity.setCreatedBy(UserUtil.getCode());
        entity.setCreatedName(partyCache.translate(UserUtil.getCode()));
        entity.setModifiedBy(UserUtil.getCode());
        entity.setModifiedName(partyCache.translate(UserUtil.getCode()));

        return super.save(entity);
    }
    public void checkCustomerCodeAndCustomerName(Container entity){
        //同一机构下条码不能重复
        ContainerDTO dto = new ContainerDTO();
        dto.setType(entity.getType());
        dto.setGroupCode(UserUtil.getBranchCode());
        dto.setType("");
        dto.setBarCode(entity.getBarCode());
        int code = baseMapper.selectCount(new QueryWrapper<Container>().lambda().eq(Container::getBarCode, entity.getBarCode()).eq(Container::getGroupCode,UserUtil.getBranchCode()));
        if (code > NumConst.NUM_ZERO) {
                throw new ServiceException(THE_CONTAINER_NUMBER_ALREADY_EXISTS);
        }
    }

    /**
     * 根据code更新实体
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor={Exception.class})
    public int updateByCode(Container entity) {
        if(StringUtils.isBlank(entity.getModifiedBy())){
            entity.setModifiedBy(UserUtil.getCode());
            entity.setModifiedName(partyCache.translate(UserUtil.getCode()));
        }else {
            entity.setModifiedName(partyCache.translate(entity.getModifiedBy()));
        }

        return baseMapper.update(entity,new QueryWrapper<Container>().lambda().eq(Container::getCode,entity.getCode()).eq(Container::getGroupCode,UserUtil.getBranchCode()));
    }

    /**
     * 查询列表
     * @param containerDTO
     * @return
     */
    @Override
    public List<ContainerVO> queryList(ContainerDTO containerDTO){
        //排序字段名需要驼峰转数据库下划线类型字段名
        if(StringUtils.isNotEmpty(containerDTO.getSortField())){
            containerDTO.setSortField(CommonUtil.camel2Underline(containerDTO.getSortField()));

            if ("type_name".equals(containerDTO.getSortField())){
                containerDTO.setSortField("a.type");
            }
        }
        List<ContainerVO> containerVOList = baseMapper.queryList(containerDTO);
        containerVOList.forEach(info ->{
            ContainerTypeEnum enumByValue = ContainerTypeEnum.getEnumByValue(info.getType());
            info.setTypeName(enumByValue.getDesc());
        });
        return containerVOList;
    }

    /**
     * 查询合计,包含总数
     * @param containerDTO
     * @return
     */
    @Override
    public ContainerVO queryListSum(ContainerDTO containerDTO){
        ContainerVO containerVO = baseMapper.queryListSum(containerDTO);
        return containerVO;
    }


    /**
     * 批量删除
     * @param codes
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public Map<String,Object> batchdel(List<String> codes) {

        Map<String,Object> result = new HashMap<>();
        //根据codes查询容器信息；
        List<ContainerVO> containerVos = queryByCodes(codes);
        //容器编码map 登录机构
        Map<String, String> barCodeMap = containerVos.stream().collect(Collectors.toMap(ContainerVO::getCode, ContainerVO::getBarCode));
        //不可删除code
        List<String> notDeleteCodes = containerVos.stream().filter(info -> info.getDeleteFlag().intValue() == DeleteFlagEnum.NOTDELETE.getCode().intValue()).map(info1 -> {
            return info1.getCode();
        }).collect(Collectors.toList());
        int num = 0;
        if(IterUtil.isNotEmpty(notDeleteCodes)){
            List<String> notDeleteBarCode = notDeleteCodes.stream().map(code -> {
                return barCodeMap.get(code);
            }).collect(Collectors.toList());
            result.put("notDelete",notDeleteBarCode);
            //可删除code
            List<String> deleteCodes = codes.stream().filter(r -> !notDeleteCodes.contains(r)).collect(Collectors.toList());
            num = delete(deleteCodes);
        }else {
            num = delete(codes);
        }
        result.put("success",num);
        return result;
    }
    private int delete(List<String> codes){
        //可删除code
        int num = 0;
        if(IterUtil.isNotEmpty(codes)){
            for(String code : codes){
                this.update(new UpdateWrapper<Container>().lambda().eq(Container::getCode, code).eq(Container::getGroupCode,UserUtil.getBranchCode())
                        .set(Container::getDeleted, 1));
                num++;
            }
        }
        return num;
    }

    /**
     *  根据code更新启用、停用状态
     * @param codes
     * @param active
     * @return
     */
    @Override
    public Map<String,Object> activeStatus(List<String> codes, StatusEnum active) {
        Map<String,Object> result= new HashMap<>();
        int num = 0;
        if(active.getValue().equals(StatusEnum.UNACTIVE.getValue())){
            List<ContainerVO> containerVos = queryByCodes(codes);
            //容器编码map
            Map<String, String> barCodeMap = containerVos.stream().collect(Collectors.toMap(ContainerVO::getCode, ContainerVO::getBarCode));
            //已占用code，不能停用
            List<String> notOptCodes = containerVos.stream().filter(info -> ("1").equals(info.getOccupy())).map(info -> {
                return info.getCode();
            }).collect(Collectors.toList());
            List<String> barCodes = notOptCodes.stream().map(code -> {
                return barCodeMap.get(code);
            }).collect(Collectors.toList());
            result.put("barcodes",barCodes);
            List<String> optCodes = codes.stream().filter(r -> !notOptCodes.contains(r)).collect(Collectors.toList());
            num = updateStatus(optCodes,active);
        }else {
            num = updateStatus(codes,active);
        }
        result.put("success",num);
        return result;
    }

    private int updateStatus(List<String> optCodes,StatusEnum active){
        int num = 0;
        boolean flag = false;
        if(IterUtil.isNotEmpty(optCodes)){
            for(String code: optCodes){
                if(active.getValue().equals(StatusEnum.ACTIVE.getValue())){
                    flag = this.update(new UpdateWrapper<Container>().lambda().set(Container::getStatus, active).set(Container::getDeleteFlag, DeleteFlagEnum.NOTDELETE.getCode())
                            .eq(Container::getCode, code).eq(Container::getGroupCode,UserUtil.getBranchCode()));
                }else {
                    flag = this.update(new UpdateWrapper<Container>().lambda().set(Container::getStatus, active)
                            .eq(Container::getCode, code).eq(Container::getGroupCode,UserUtil.getBranchCode()));
                }
                if(flag){
                    num++;
                }
            }
        }
        return num;
    }

    @Override
    public List<ContainerVO> queryByBarCode(String barCode) {

        return baseMapper.queryByBarCode(barCode,UserUtil.getBranchCode());
    }

    @Override
    public List<ContainerVO> queryByCodes(List<String> list) {
        return baseMapper.queryByCodes(list, UserUtil.getBranchCode());
    }

    /**
     * 查询可用容器，如果收货单编码存在，则也可使用该收货单占用的容器
     *
     * @param receiveCode
     * @return
     */
    @Override
    public List<Container> queryCanUseList(String receiveCode) {
        //可用容器有两部分组成，分别是没有用过的容器和用过的容器但是收货批次为空的
        //没有用过的容器
        List<Container> result = list(new QueryWrapper<Container>().lambda()
                    .eq(Container::getStatus, StatusEnum.ACTIVE.getValue())
                    .eq(Container::getDeleted, "0")
                    .eq(Container::getOccupy, "0")
                    .eq(Container::getGroupCode,UserUtil.getBranchCode()));
        //用过的容器但是收货批次不为空的
        List<ReceiveRegister> receiveRegisters = receiveRegisterService.list(new QueryWrapper<ReceiveRegister>().lambda().eq(ReceiveRegister::getReceiveCode,receiveCode)
                .isNotNull(ReceiveRegister::getContainerBarCode)
                .isNull(ReceiveRegister::getReceiveLot));
        for (ReceiveRegister receiveRegister : receiveRegisters) {
            Container container = getOne(new QueryWrapper<Container>().lambda().eq(Container::getBarCode,receiveRegister.getContainerBarCode()).eq(Container::getGroupCode,UserUtil.getBranchCode()));
            if (ObjectUtil.isNotNull(container)) {
                result.add(container);
            }
        }
        // 去重
        result = result.stream().filter( distinctByKey(Container::getCode)).collect( Collectors.toList() );
        return result;
    }

    /**
     * 占用容器
     *
     * @param occupyCode
     * @param containerBarCode
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public Result occupyContainer(String occupyCode, String containerBarCode,String groupCode){
        String errMsg = "占用容器失败";

        Container container = getOne(new QueryWrapper<Container>().lambda()
                .eq(Container::getGroupCode,groupCode)
                .eq(Container::getBarCode, containerBarCode).eq(Container::getDeleted, 0));
        if(container == null){
            errMsg = "容器不存在，请确认";
            throw new ServiceException(errMsg);
        }
        if (!StatusEnum.ACTIVE.getValue().equals(container.getStatus())) {
            errMsg = "容器未启用";
            throw new ServiceException(errMsg);
        }

        // 更新占用状态及占用的单据号
        //boolean flag = this.update(new UpdateWrapper<Container>().lambda()
        //        .eq(Container::getStatus, StatusEnum.ACTIVE.getValue())
        //        .eq(Container::getDeleted, "0")
        //        .eq(Container::getBarCode, containerBarCode)
        //        // 如果是其他单据占用，则查询不到，返回结果为false，保证占用时不冲突
        //        .and(wrapper->wrapper.eq(Container::getOccupyCode, StrUtil.EMPTY)
        //                .or().eq(Container::getOccupyCode, null)
        //                .or().eq(Container::getOccupyCode, occupyCode)
        //        .set(Container::getOccupy, "1")
        //        .set(Container::getOccupyCode, occupyCode)));

        int count = this.baseMapper.updateStatus(containerBarCode,occupyCode,groupCode);

        // 占用失败时，判断失败原因
        if(count<1) {
            String dbOccupyCode = container.getOccupyCode();
            if (!StatusEnum.ACTIVE.getValue().equals(container.getStatus())) {
                errMsg = "容器未启用";
                throw new ServiceException(errMsg);
            } else if (StrUtil.isNotBlank(dbOccupyCode) && !StrUtil.equals(dbOccupyCode, occupyCode)) {
                errMsg = "容器已被其他单据占用";
                throw new ServiceException(errMsg);
            }
            //下面的代码没意义，还容易引起查询收货登记信息时查询多条数据的问题
           /* else if (StrUtil.isNotBlank(dbOccupyCode)) {
                ReceiveRegister receiveRegister = receiveRegisterService.getOne(new LambdaQueryWrapper<ReceiveRegister>()
                        .eq(ReceiveRegister::getReceiveCode, dbOccupyCode)
                        .eq(ReceiveRegister::getContainerBarCode, containerBarCode));
                String goodsName = goodsCache.translate(receiveRegister.getGoodsCode());
                ReceiveRegister receiveRegister1 = receiveRegisterService.getOne(new LambdaQueryWrapper<ReceiveRegister>()
                        .eq(ReceiveRegister::getReceiveCode, occupyCode)
                        .eq(ReceiveRegister::getContainerBarCode, containerBarCode));
                if (!receiveRegister.getGoodsCode().equals(receiveRegister1.getGoodsCode())) {
                    errMsg = "容器已被商品" + goodsName + "占用";
                    throw new ServiceException(errMsg);
                }

            }*/
        }

        return Result.ok().data(container);
    }

    /**
     * 波次单占用容器
     * @param occupyCode
     * @param containerBarCode
     * @return
     */
    @Override
    public Result wavePickingOccupyContainer(String occupyCode, String containerBarCode){
        String errMsg = "占用容器失败";
        String groupCode = UserUtil.getBranchCode();

        Container container = getOne(new QueryWrapper<Container>().lambda()
                .eq(Container::getBarCode, containerBarCode)
                .eq(Container::getGroupCode,groupCode)
                .eq(Container::getDeleted, 0));
        if(container == null){
            errMsg = "容器不存在，请确认";
            throw new ServiceException(errMsg);
        }

        int count = this.baseMapper.updateStatus(containerBarCode,occupyCode, groupCode);

        // 占用失败时，判断失败原因
        if(count<1) {
            String dbOccupyCode = container.getOccupyCode();
            if (!StatusEnum.ACTIVE.getValue().equals(container.getStatus())) {
                errMsg = "容器未启用";
                throw new ServiceException(errMsg);
            }

            if (StrUtil.isNotBlank(dbOccupyCode) && !StrUtil.equals(dbOccupyCode, occupyCode)) {
                errMsg = "容器已被其他单据占用";
                throw new ServiceException(errMsg);
            }
        }
        return Result.ok().data(container);
    };
    /**
     * 释放容器，并清除占用单据号， 不判断容器是否可释放
     * @param containerBarCode
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public boolean releaseContainer(String containerBarCode,String groupCode) {
        return this.update(new UpdateWrapper<Container>().lambda()
                .eq(Container::getGroupCode,groupCode)
                .eq(Container::getBarCode, containerBarCode)
                .set(Container::getOccupyCode, StrUtil.EMPTY)
                .set(Container::getOccupy, "0")
        );
    }

    /**
     * @param containerBarCodes :
     * @description 批量释放容器，并清除占用单据号， 不判断容器是否可释放
     * @author xuyang
     * @date 2020/11/6 14:35
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean batchReleaseContainer(List<String> containerBarCodes) {
        if(IterUtil.isNotEmpty(containerBarCodes)) {
            return this.update(new UpdateWrapper<Container>().lambda()
                    .eq(Container::getGroupCode,UserUtil.getBranchCode())
                    .in(Container::getBarCode, containerBarCodes)
                    .set(Container::getOccupyCode, StrUtil.EMPTY)
                    .set(Container::getOccupy, "0"));
        }
        return true;
    }

    /**
     * @param containerBarCodes :
     * @description 判断容器号是否还有库存使用，没有则释放
     * @author xuyang
     * @date 2020/11/6 14:42
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void batchReleaseContainerForEmptyInventory(List<String> containerBarCodes,String groupCode) {
        if(IterUtil.isNotEmpty(containerBarCodes)) {
            // 去重，减少循环
            containerBarCodes = containerBarCodes.stream().distinct().collect(Collectors.toList());
            // 查询该容器是否有库存占用的sql模板
            String conditionTemplate = " select 1 from wms_inventory t where t.container_bar_code = '{}' and t.quantity > 0 ";
            String conditionSql;
            for (String barCode : containerBarCodes) {
                if (StrUtil.isNotBlank(barCode)){
                    conditionSql = StrUtil.format(conditionTemplate, barCode);
                    this.update(new UpdateWrapper<Container>().lambda()
                            .eq(Container::getGroupCode,groupCode)
                            .eq(Container::getBarCode, barCode)
                            .set(Container::getOccupyCode, StrUtil.EMPTY)
                            .set(Container::getOccupy, "0")
                            .notExists(conditionSql));
                }
            }
        }
    }

    /**
     * 根据收货单、容器号集合拼接的字符串，释放该收货单下没有可用收货登记信息关联的容器
     *
     * @param receiveCode
     * @param containerBarCodes
     * @param type 容器类型，空字符串： 所有； 0：托盘；1：周转箱；
     * @return
     */
    @Override
    public boolean batchReleaseContainerByReceive(String receiveCode, String containerBarCodes, String type) {
        return this.baseMapper.batchReleaseContainerByReceive(receiveCode, containerBarCodes, type,UserUtil.getBranchCode());
    }

    /**
     * 根据上架单、释放该上架单使用的容器
     *
     * @param putawayCode
     * @param type  容器类型，空字符串： 释放所有； 0：释放托盘类型的容器；1：释放周转箱类型的容器；
     * @return
     */
    @Override
    public boolean batchReleaseContainerByPutaway(String putawayCode, String type,String groupCode) {
        return this.baseMapper.batchReleaseContainerByPutaway(putawayCode, type,groupCode);
    }


    /**
     * 获取库位导入操作预览数据
     * @param list
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public List<ContainerVO> importPreviewList(List<ContainerExcelDTO> list) {
        // 校验必填项是否填写
        validateImportNotBlank(list);
        // 校验Excel里是否有重复数据
        validateImportRepeatData(list);
        // 导入时的code值，将待导入数据放到缓存中，可以用它获取回来
        String importCode = IdUtil.simpleUUID();

        // 遍历
        List<ContainerVO> containerVos = IntStream.range(NUM_ZERO, list.size()).mapToObj(index -> {
            ContainerExcelDTO item = list.get(index);
            ContainerVO vo = new ContainerVO();
            // 校验容器类型
            ContainerTypeEnum containerTypeEnum = ContainerTypeEnum.getEnumByDesc(item.getTypeName());
            if (containerTypeEnum == null) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + CONTAINER_TYPE + NOT_EXIST);
            }

            // 生成预览数据
            BeanUtil.copyProperties(item, vo);
            vo.setType(containerTypeEnum.getValue());
            vo.setImportCode(importCode);

            return vo;
        }).collect(Collectors.toList());

        // 保存到缓存中
        importTempCache.putTempData(importCode, containerVos);

        return containerVos;
    }

    /**
     * 根据导入码导入库位信息
     *
     * @param importCode
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result importContainer(String importCode) {
        // 根据导入码，获取导入缓存
        List<Container> importCache = importTempCache.getTempData(importCode, Container.class);
        // 判断数据，如果没有了，则直接返回
        if(importCache == null){
            return Result.error("缓存数据不存在，或超过有效时间，缓存已被清除，请重新导入。");
        }
        // 遍历保存
        importCache.forEach(e->{
            // 保存或更新数据
            importSaveOrUpdate(e);
        });
        // 清除缓存， 容器暂时没缓存，略过
        // 完成导入后删除导入的缓存
        importTempCache.removeCache(importCode);

        return Result.ok();
    }

    /**
     * 导入时，根据编码判断是插入还是更新
     * @param entity
     */
    @Transactional(rollbackFor = {Exception.class})
    public void importSaveOrUpdate(Container entity){
        // 查询数据库，判断是否有相同库位编码数据
        Container sameOne = this.getOne(new QueryWrapper<Container>().lambda().eq(Container::getBarCode, entity.getBarCode()).eq(Container::getGroupCode,UserUtil.getBranchCode()));
        if(sameOne == null){
            // 没有相同数据，添加必要数据，插入
            entity.setStatus(StatusEnum.UNACTIVE.getValue());

            save(entity);
        }else{
            // 有相同数据，执行更新操作
            this.update(new UpdateWrapper<Container>().lambda().eq(Container::getCode, sameOne.getCode()).eq(Container::getGroupCode,UserUtil.getBranchCode())
                    .set(Container::getType, entity.getType())
                    .set(Container::getRemarks, entity.getRemarks()));
        }
    }

    /**
     * 校验导入数据必填项是否有空值
     * @param excelDTOList
     */
    private void validateImportNotBlank(List<ContainerExcelDTO> excelDTOList) {
        IntStream.range(NUM_ZERO, excelDTOList.size()).forEach(index -> {
            if (StrUtil.isBlank(excelDTOList.get(index).getBarCode())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + CONTAINER_BAR_CODE + NOT_BLANK);
            }

            if (StrUtil.isBlank(excelDTOList.get(index).getTypeName())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + CONTAINER_TYPE + NOT_BLANK);
            }

        });
    }


    /**
     * 校验Excel中是否有重复数据
     * @param excelDTOList
     */
    private void validateImportRepeatData(List<ContainerExcelDTO> excelDTOList) {
        List<ContainerExcelDTO> itemExcelList = excelDTOList;
        Stream.iterate(NUM_ZERO, index -> index + NUM_ONE).limit(itemExcelList.size()).forEach(index -> {
            Stream.iterate(NUM_ZERO, indexJ -> indexJ + NUM_ONE).limit(itemExcelList.size()).forEach(indexJ -> {
                // 校验容器号
                boolean barCodeFlag = excelDTOList.get(index).getBarCode().equals(itemExcelList.get(indexJ).getBarCode());
                // 相同容器号，并且不是相同行
                if(barCodeFlag  && !index.equals(indexJ)) {
                    throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + AND_NAME + TIP_PREFIX + (indexJ + NUM_THREE) + LINE + REPEAT_DATA);
                }
            });
        });
    }

    /**
     * 列表去重
     * @param keyExtractor
     * @param <T>
     * @return
     */
    public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor){
        Map<Object, Boolean> map = new ConcurrentHashMap<>();
        return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    /**
     * 占用容器
     *
     * @param occupyCode
     * @param containerBarCode
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public Result occupyContainerByPicking(String occupyCode, String containerBarCode){
        String errMsg = "占用容器失败";
        // 更新占用状态及占用的单据号
        boolean flag = this.update(new UpdateWrapper<Container>().lambda()
                .eq(Container::getStatus, StatusEnum.ACTIVE.getValue())
                .eq(Container::getDeleted, "0")
                .eq(Container::getGroupCode,UserUtil.getBranchCode())
                .eq(Container::getBarCode, containerBarCode)
                // 如果是其他单据占用，则查询不到，返回结果为false，保证占用时不冲突
                .and(wrapper->wrapper.eq(Container::getOccupyCode, "").or().eq(Container::getOccupyCode, occupyCode))
                .set(Container::getOccupy, "1")
                .set(Container::getOccupyCode, occupyCode));
        // 占用失败时，判断失败原因
        if(!flag){
            Container container = getOne(new QueryWrapper<Container>().lambda()
                    .eq(Container::getGroupCode,UserUtil.getBranchCode())
                    .eq(Container::getBarCode, containerBarCode).eq(Container::getDeleted, 0));
            if(container == null){
                errMsg = "容器不存在，请确认";
            }

            String dbOccupyCode = container.getOccupyCode();
            if(!StatusEnum.ACTIVE.getValue().equals(container.getStatus())){
                errMsg = "容器未启用";
            }else if(StrUtil.isNotBlank(dbOccupyCode)&&StrUtil.equals(dbOccupyCode, occupyCode)){
                errMsg = "容器已被其他单据占用";
            }

            return Result.error(errMsg);
        }else {

            return Result.ok();
        }
    }

    /**
     * 校验容器
     * @param containerBarCode
     * @param sourceCode
     * @return
     */
    @Override
   public Result validateContainer( String containerBarCode, String sourceCode,String groupCode) {
        Map<String, Object> resultMap = new HashMap<>();
        //containerBarCode的非空校验
        if (StrUtil.isEmpty(containerBarCode)) {
            resultMap.put("isValidate", false);
            resultMap.put("message", "请输入或扫描容器号！！！");
            return Result.ok().data(resultMap);
        }
        Container container = this.getOne(new LambdaQueryWrapper<Container>().eq(Container::getOccupyCode, sourceCode).eq(Container::getGroupCode,groupCode));


        if (null != container) {
            //判断新的容器条码是否与拣货单占用的容器条码一致，不一致则给提示，一致直接返回成功
            if (container.getBarCode().equals(containerBarCode)) {
                return Result.ok();
            } else {
                resultMap.put("isValidate", false);
                resultMap.put("message", "新的容器条码" + containerBarCode + "与该拣货单已占用的容器条码" + container.getBarCode() + "不一致，请确认！！！");
                return Result.ok().data(resultMap);
            }
        }
        container = this.getOne(new QueryWrapper<Container>().lambda().eq(Container::getBarCode, containerBarCode)
                .eq(Container::getGroupCode,groupCode)
                .eq(Container::getStatus, StatusEnum.ACTIVE.getValue())
                .eq(Container::getDeleted, "0")
                .eq(Container::getOccupy, "0"));
        if (ObjectUtil.isNull(container)) {
            resultMap.put("isValidate", false);
            resultMap.put("message", "容器未启用或者容器已被占用或者容器不存在，请确认！！！");
            return Result.ok().data(resultMap);
        }
        return Result.ok();
    }
}
